package io.github.mengfly.kvstore.client.cli;

import io.github.mengfly.kvstore.KvStoreFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * ${COMMON}
 *
 * @author wangp
 */
public class Main {
    public static void main(String[] args) throws Exception {

        if (args == null || args.length < 1) {
            System.err.println("Please provide a valid store file path.");
            return;
        }
        KvStoreFile file = new KvStoreFile(new File(args[0]));

        Scanner scanner = new Scanner(System.in);

        handleHelp("welcome");

        while (true) {
            System.out.print("> ");
            final String next = scanner.nextLine();

            if (next == null || next.length() == 0) {
                continue;
            }
            final String[] split = next.split("\\s+");
            String op = split[0].toLowerCase();
            switch (op) {
                case "h":
                case "help":
                    handleHelp(null);
                    break;
                case "put":
                    handlePut(file, split);
                    break;
                case "get":
                    handleGet(file, split);
                    break;
                case "del":
                    handleDelete(file, split);
                    break;
                case "merge":
                    handleMerge(file, split);
                    break;
                case "exit":
                    System.exit(0);
                    break;
                case "cls":
                case "clear":
                    System.out.print("\033[H\033[2J");
                    System.out.flush();
                    break;
                case "keys":
                    handleKeys(file, split);
                    break;
                default:
                    System.err.println("Unknown argument type " + op);
            }
        }
    }

    private static void handleKeys(KvStoreFile file, String[] split) {
        checkArg(split, 0, () -> {
            final List<String> keys = file.keys();
            AtomicInteger index = new AtomicInteger(1);
            System.out.println(keys.stream()
                    .map(s -> String.format("%d) %s", index.getAndIncrement(), s))
                    .collect(Collectors.joining("\n")));
        });
    }

    private static void handleDelete(KvStoreFile file, String[] split) {
        checkArg(split, 1, () -> {
            if (!file.del(split[1])) {
                System.err.println("key not exists: " + split[1]);
            }
        });
    }

    private static void handleMerge(KvStoreFile file, String[] split) {
        checkArg(split, 0, file::merge);
    }

    private static void handleGet(KvStoreFile file, String[] split) {
        checkArg(split, 1, () -> {
            final String entry = file.get(split[1]);
            System.out.println(entry);
        });
    }


    private static void handlePut(KvStoreFile file, String[] split) {
        checkArg(split, 2, () -> file.put(split[1], split[2]));
    }

    private static void handleHelp(String type) {
        final String helpString = getHelpString(type);
        if (helpString != null) {
            System.out.println(helpString);
        }
    }

    private static void checkArg(String[] split, int length, Exec exec) {
        if (split.length == 2 && (split[1].equals("-h") || split[1].equals("--help"))) {
            handleHelp(split[0]);
            return;
        }
        if (split.length != length + 1) {
            System.err.printf("%s require arg is %d current length is %d\n", split[0], length, split.length - 1);
            return;
        }
        try {
            final long start = System.nanoTime();
            exec.run();
            final long end = System.nanoTime();
            System.out.println("OK " + (end - start) + " nanos");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    interface Exec {
        void run() throws Exception;
    }

    private static String getHelpString(String arg) {
        if (arg == null || arg.length() == 0) {
            arg = "help";
        }

        try (InputStream is = Main.class.getClassLoader().getResourceAsStream("help/" + arg.toLowerCase() + ".txt")) {
            if (is == null) {
                System.err.println("not found " + arg + " documentation");
                return null;
            }
            return readStreamString(is);
        } catch (Exception e) {
            System.err.println("not found " + arg + " documentation");
            return null;
        }
    }

    private static String readStreamString(InputStream is) throws IOException {
        byte[] bytes = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = is.read(bytes)) > 0) {
            sb.append(new String(bytes, 0, len, StandardCharsets.UTF_8));
        }
        return sb.toString();
    }

}